

#include "../../canon/log.h"
#include "../../canon/config.h"
#include "yaml-cpp/yaml.h"

#if 1
void test_config()
{
#define XX(g_var, name, prefix)                                                    \
    {                                                                              \
        auto v = g_var->getValue();                                                \
        for (auto &i : v) {                                                        \
            CANON_LOG_INFO(CANON_LOG_ROOT()) << #prefix << " " #name << ": " << i; \
        }                                                                          \
    }

#define XX_M(g_var, name, prefix)                                                            \
    {                                                                                        \
        auto v = g_var->getValue();                                                          \
        for (auto &i : v) {                                                                  \
            CANON_LOG_INFO(CANON_LOG_ROOT()) << #prefix << " " #name << ": "                 \
                                             << " {" << i.first << " - " << i.second << "}"; \
        }                                                                                    \
    }

    canon::ConfigVar<int>::ptr intConfig = canon::Config::Lookup("system.port", (int)8080, "system port");
    auto int_list_config = canon::Config::Lookup("system.int_list", std::list<int>{1, 2, 3}, "system int list");
    auto int_vec_config = canon::Config::Lookup("system.int_vec", std::vector<int>{4, 5, 6}, "system int vec");
    auto int_set_config = canon::Config::Lookup("system.int_set", std::set<int>{11, 22, 33}, "system int set");
    auto int_uset_config =
        canon::Config::Lookup("system.int_uset", std::unordered_set<int>{121, 232, 343}, "system int uset");
    auto int_map_config =
        canon::Config::Lookup("system.int_map", std::map<std::string, int>{{"a", 2}, {"b", 78}}, "system int map");
    auto int_umap_config = canon::Config::Lookup(
        "system.int_umap", std::unordered_map<std::string, int>{{"aa", 22}, {"bb", 748}}, "system int umap");
    CANON_LOG_INFO(CANON_LOG_ROOT()) << "before int: " << intConfig->getValue();

    XX(int_list_config, intlist, before);
    XX(int_vec_config, int_vec, before);
    XX(int_set_config, int_set, before);
    XX(int_uset_config, int_uset, before);
    XX_M(int_map_config, int_map, before);
    XX_M(int_umap_config, int_umap, before);

    YAML::Node node = YAML::LoadFile("./config/log.yaml");
    canon::Config::LoadFromYaml(node);

    CANON_LOG_INFO(CANON_LOG_ROOT()) << "after int: " << intConfig->getValue();

    XX(int_list_config, int_list, after);
    XX(int_vec_config, int_vec, after);
    XX(int_set_config, int_set, after);
    XX(int_uset_config, int_uset, after);
    XX_M(int_map_config, int_map, after);
    XX_M(int_umap_config, int_umap, after);
}

#endif

class Person
{
public:
    explicit Person() = default;
    std::string m_name;
    int m_age{};
    ;
    bool m_sec{};

    [[nodiscard]] std::string toString() const
    {
        std::stringstream ss;
        ss << "[Person name = " << m_name << " age = " << m_age << " sec = " << m_sec << "]";
        return ss.str();
    }

    bool operator==(const Person &p) const { return m_name == p.m_name && m_age == p.m_age && m_sec == p.m_sec; }
};

namespace canon {

template <>
class LexicalCast<std::string, Person>
{
public:
    Person operator()(const std::string &v)
    {
        YAML::Node node = YAML::Load(v);
        Person p;
        p.m_name = node["name"].as<std::string>();
        p.m_age = node["age"].as<int>();
        p.m_sec = node["sec"].as<bool>();
        return p;
    }
};

template <>
class LexicalCast<Person, std::string>
{
public:
    std::string operator()(const Person &p)
    {
        YAML::Node node;
        node["name"] = p.m_name;
        node["age"] = p.m_age;
        node["sec"] = p.m_sec;
        std::stringstream ss;
        ss << node;
        return ss.str();
    }
};
} // namespace canon

void test_class()
{
    canon::ConfigVar<Person>::ptr person = canon::Config::Lookup("class.person", Person(), "system person");

    person->addListener([&](const Person &old_value, const Person &new_value) {
        CANON_LOG_INFO(CANON_LOG_ROOT()) << "old value = " << old_value.toString();
        CANON_LOG_INFO(CANON_LOG_ROOT()) << "new value = " << new_value.toString();
    });

    CANON_LOG_INFO(CANON_LOG_ROOT()) << "before class: " << person->getValue().toString();

    YAML::Node node = YAML::LoadFile("./config/log.yaml");
    canon::Config::LoadFromYaml(node);

    CANON_LOG_INFO(CANON_LOG_ROOT()) << "after class: " << person->getValue().toString();
}

void test_log_config()
{
    static canon::Logger::ptr system_log = CANON_LOG_NAME("q");
    CANON_LOG_INFO(system_log) << "hello system";
    std::cout << canon::LoggerMgr::GetInstance()->toYamlString() << std::endl;
    YAML::Node root = YAML::LoadFile("./config/log.yaml");
    canon::Config::LoadFromYaml(root);
    std::cout << "================" << std::endl;
    std::cout << canon::LoggerMgr::GetInstance()->toYamlString() << std::endl;
    std::cout << "================" << std::endl;
    std::cout << root << std::endl;
    CANON_LOG_INFO(system_log) << "hello system" << std::endl;

    system_log->setFormatter("%d - %m%n");
    CANON_LOG_INFO(system_log) << "hello system";
}

int main()
{
    // test_class();
    // test_config();
    test_log_config();

    return 0;
}
